代理的概念:给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用。在对目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制。
// 目标对象
const target = {
id: 'target'
};
// 代理对象
const handler = {} // 空的代理方法
const proxy = new Proxy(target, handler);
// 通过代理访问对象的值
console.log(target.id); // target
console.log(proxy.id); // target
// 给目标对象赋值,也会反应到代理对象上
target.id = 'foo';
console.log(target.id); // foo
console.log(proxy.id); // foo
// 给代理对象赋值,也会反映到目标对象上
proxy.id = 'bar';
console.log(target.id); // bar
console.log(proxy.id); // bar
// 但是两者是不同的
console.log(target === proxy); // false
使用代理的主要目的是可以定义捕获器,每个捕获器都对应一种基本操作,可以直接 或间接在代理对象上调用。在代理对象上调用这些操作时,代理会先调用捕获器函数,从而拦截并修改相应的行为。
// 目标对象
const target = {
foo: 'bar'
};
// 代理对象
const handler = {
// 给代理对象添加捕获器
get() {
return 'handler override';
}
};
const proxy = new Proxy(target, handler);
// 通过代理访问参数就会触发捕获器
console.log(target.foo); // bar
console.log(proxy.foo); // handler override
get() 捕获器会接收三个参数:目标对象、要查询的属性和代理对象,基于这些参数可以重新设计 get() 方法的行为
// 目标对象
const target = {
foo: 'bar'
};
// 代理对象
const handler = {
// 给代理对象添加捕获器
get(trapTarget, property, receiver) {
console.log(trapTarget === target);
console.log(property);
console.log(receiver === proxy);
}
};
const proxy = new Proxy(target, handler);
proxy.foo;
// true
// foo
// true
所有捕获器都可以基于自己的参数重建原始操作,但并非所有捕获器行为都像 get() 那么简单。通过调用全局 Reflect 对象上的同名方法来获取原始行为
const target = {
foo: 'bar'
};
const handler = {
get() {
return Reflect.get(...arguments);
}
};
// handler 也可以简写为
const handler = {
get: Reflect.get
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo); // bar
console.log(target.foo); // bar
如果仅仅是给目标对象套一层空代理就有些多此一举了,一般是对想要查询或修改的属性进行一些前置操作
const target = {
foo: 'bar',
baz: 'qux'
};
const handler = {
get(trapTarget, property, receiver) {
let decoration = '';
if (property === 'foo') {
decoration = '!!!';
}
return Reflect.get(...arguments) + decoration;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo); // bar!!!
console.log(target.foo); // bar
console.log(proxy.baz); // qux
console.log(target.baz); // qux
对于使用 new Proxy() 创建的普通代理来说,可以通过 revocable() 和 revoke() 方法中断其与目标对象的联系。
const target = {
foo: 'bar'
};
const handler = {
get() {
return 'intercepted';
}
};
// 创建可撤销的代理对象
const { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.foo); // intercepted
console.log(target.foo); // bar
revoke(); // 中断代理和目标之间的联系
console.log(proxy.foo); // 提示 TypeError 错误
代理可以拦截反射 API 的操作,这意味着可以创建一个代理,通过它去代理另一个代理。这样就可以在一个目标对象之上构建多层拦截网:
const target = {
foo: 'bar'
};
// 第一层代理
const firstProxy = new Proxy(target, {
get() {
console.log('first proxy');
return Reflect.get(...arguments);
}
});
// 第二层代理
const secondProxy = new Proxy(firstProxy, {
get() {
console.log('second proxy');
return Reflect.get(...arguments);
}
});
console.log(secondProxy.foo);
// second proxy
// first proxy
// bar